#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/5/20 15:15
# @Author  : Grayson Liu
# @Email   : graysonliu@foxmail.com
# @File    : rule.py
import math

from dw.dw import DwUtil
import pandas as pd
import numpy as np


def rate_rule(processor: str, mean: float, std: float, dw: DwUtil, name: str) -> float:
    """
    用于rate类型分数的标准分处理逻辑
    :param processor: 处理器名称
    :param mean: 所有分数的均值（平均到每一个核上的）
    :param std: 所有分数的标准差（平均到每一个核上的）
    :param dw: 传入的数据仓库层工具DwUtil的实例，用于查询数据库
    :param name: benchmark/rate类型分数名称，例如cpu2006_fprate等
    :return: 该处理器该项的标准分
    """
    # 1. 选出指定处理器该项分数的记录，其中包括原始分与核数
    result_list = dw.execute_sql("""
    SELECT result, num_core FROM dw_{} WHERE processor='{}' AND result!=0.0;
    """.format(name, processor))
    # 2. 如果找不到，返回缺失值NaN
    if result_list.empty:
        return np.NaN
    # 3. 如果记录不为空
    else:
        # 3.1 rate类型分数一般表示吞吐量，与核数成正相关，因此将分数平均到每一个核上
        result_list["core_result"] = result_list["result"] / result_list["num_core"]
        # 3.2 根据传入的均值与方差，计算标准分z，服从N(0,1)
        std_result = (result_list["core_result"].mean() - mean) / std
        # 3.3 标准分z有正有负，因此化为均值为50，标准差为10的正态分布分数，返回该分数
        report_result = 50 + (10 * std_result)
        return report_result


def speed_rule(processor: str, mean: float, std: float, dw: DwUtil, name: str) -> float:
    """
    用于非rate类型分数的标准分处理逻辑
    :param processor: 处理器名称
    :param mean: 所有分数的均值
    :param std: 所有分数的标准差
    :param dw: 传入的数据仓库层工具DwUtil的实例，用于查询数据库
    :param name: benchmark/rate类型分数名称，例如cpu2006_fpspeed等
    :return: 该处理器该项的标准分
    """
    # 1. 选出指定处理器该项分数的记录，只需要原始分
    result_list = dw.execute_sql("""
        SELECT result FROM dw_{} WHERE processor='{}' AND result!=0.0;
        """.format(name, processor))
    # 2. 如果找不到，返回缺失值NaN
    if result_list.empty:
        return np.NaN
    # 3. 如果记录不为空，仿照上个函数进行操作，先转换为标准分z再计算为转化后的正态分布分数
    else:
        report_result = (result_list["result"].mean() - mean) / std
        report_result = 50 + (10 * report_result)
        return report_result


def score_to_grade(score: float) -> str:
    if score >= 70:
        return "A"
    elif 60 <= score < 70:
        return "B"
    elif 50 <= score < 60:
        return "C"
    elif 40 <= score < 50:
        return "D"
    elif 30 <= score < 40:
        return "E"
    else:
        return "F"


def int_grade_rule(line: pd.Series) -> str:
    """
    评价处理器整数运算性能，考察：
    cpu2006_intrate 10%
    cpu2006_intspeed 10%
    cpu2017_intrate 40%
    cpu2017_intspeed 40%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    weight = 0
    total = 0
    if not math.isnan(line["cpu2006_intrate"]):
        total += line["cpu2006_intrate"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2006_intspeed"]):
        total += line["cpu2006_intspeed"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2017_intrate"]):
        total += line["cpu2017_intrate"] * 0.4
        weight += 0.4
    if not math.isnan(line["cpu2017_intspeed"]):
        total += line["cpu2017_intspeed"] * 0.4
        weight += 0.4
    if weight != 0:
        total = total / weight
        return score_to_grade(total)
    else:
        return "-"


def float_grade_rule(line: pd.Series) -> str:
    """
    评价处理器浮点运算性能，考察：
    cpu2006_fprate 10%
    cpu2006_fpspeed 10%
    cpu2017_fprate 40%
    cpu2017_fpspeed 40%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    weight = 0
    total = 0
    if not math.isnan(line["cpu2006_fprate"]):
        total += line["cpu2006_fpspeed"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2006_fpspeed"]):
        total += line["cpu2006_fpspeed"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2017_fprate"]):
        total += line["cpu2017_fprate"] * 0.4
        weight += 0.4
    if not math.isnan(line["cpu2017_fpspeed"]):
        total += line["cpu2017_fpspeed"] * 0.4
        weight += 0.4
    if weight != 0:
        total = total / weight
        return score_to_grade(total)
    else:
        return "-"


def single_grade_rule(line: pd.Series) -> str:
    """
    评价处理器单核运算性能，考察：
    cpu2006_intspeed 10%
    cpu2006_fpspeed 10%
    cpu2017_intspeed 40%
    cpu2017_fpspeed 40%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    weight = 0
    total = 0
    if not math.isnan(line["cpu2006_intspeed"]):
        total += line["cpu2006_intspeed"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2017_intspeed"]):
        total += line["cpu2017_intspeed"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2017_intspeed"]):
        total += line["cpu2017_intspeed"] * 0.4
        weight += 0.4
    if not math.isnan(line["cpu2017_fpspeed"]):
        total += line["cpu2017_fpspeed"] * 0.4
        weight += 0.4
    if weight != 0:
        total = total / weight
        return score_to_grade(total)
    else:
        return "-"


def multi_grade_rule(line: pd.Series) -> str:
    """
    评价处理器多核运算性能，考察：
    cpu2006_intrate 10%
    cpu2006_fprate 10%
    cpu2017_intrate 40%
    cpu2017_fprate 40%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    weight = 0
    total = 0
    if not math.isnan(line["cpu2006_intrate"]):
        total += line["cpu2006_intrate"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2006_fprate"]):
        total += line["cpu2006_fprate"] * 0.1
        weight += 0.1
    if not math.isnan(line["cpu2017_intrate"]):
        total += line["cpu2017_intrate"] * 0.4
        weight += 0.4
    if not math.isnan(line["cpu2017_fprate"]):
        total += line["cpu2017_fprate"] * 0.4
        weight += 0.4
    if weight != 0:
        total = total / weight
        return score_to_grade(total)
    else:
        return "-"


def java_grade_rule(line: pd.Series) -> str:
    """
    评价处理器java性能，考察：
    jbb2015_comp 30%
    jbb2015_dist 30%
    jbb2015_multi 30%
    jvm2008 10%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    weight = 0
    total = 0
    if not math.isnan(line["jbb2015_comp"]):
        total += line["jbb2015_comp"] * 0.3
        weight += 0.3
    if not math.isnan(line["jbb2015_dist"]):
        total += line["jbb2015_dist"] * 0.3
        weight += 0.3
    if not math.isnan(line["jbb2015_multi"]):
        total += line["jbb2015_multi"] * 0.3
        weight += 0.3
    if not math.isnan(line["jvm2008"]):
        total += line["jvm2008"] * 0.1
        weight += 0.1
    if weight != 0:
        total = total / weight
        return score_to_grade(total)
    else:
        return "-"


def power_grade_rule(line: pd.Series) -> str:
    """
    评价处理器功耗性能，考察：
    power_ssj2008 100%
    :param line: 传入的DataFrame的一行，包含多种分数
    :return: 评价等级A~F或未评级-
    """
    if not math.isnan(line["jbb2015_comp"]):
        return score_to_grade(line["jbb2015_comp"])
    else:
        return "-"


def overall_grade_rule(line: pd.Series) -> str:
    """
    给出处理器的整体性能，取决于6项分别的等级评分
    首先将等级转化为分数，这里有A-6, B-5, C-4, D-3, E-2, F-1，未评分为也记为1，
    对各项分数求和并除以6，得到整体分数，整体分数再转换为等级
    :param line: 传入的DataFrame的一行，包含多种分数
    :return:
    """
    rank_to_point = {"A": 6, "B": 5, "C": 4, "D": 3, "E": 2, "F": 1, "-": 0}
    points = 0
    weight = 0
    if rank_to_point[line["int_grade"]] != 0:
        points += rank_to_point[line["int_grade"]]
        weight += 1
    if rank_to_point[line["float_grade"]] != 0:
        points += rank_to_point[line["float_grade"]]
        weight += 1
    if rank_to_point[line["single_grade"]] != 0:
        points += rank_to_point[line["single_grade"]]
        weight += 1
    if rank_to_point[line["multi_grade"]] != 0:
        points += rank_to_point[line["multi_grade"]]
        weight += 1
    if rank_to_point[line["java_grade"]] != 0:
        points += rank_to_point[line["java_grade"]]
        weight += 1
    if rank_to_point[line["power_grade"]] != 0:
        points += rank_to_point[line["power_grade"]]
        weight += 1
    if weight == 0:
        return "-"
    else:
        return score_to_grade((points / weight) * 10 + 20)
